package io.sunny.platform.backend.common.aspect;

import io.sunny.platform.backend.common.annotation.Audit;
import io.sunny.platform.backend.common.entity.AuditLog;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.javers.core.Javers;
import org.javers.core.diff.Diff;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @Author SUNNY
 * @Date 2025/10/20 17:38
 * @PackageName: io.sunny.platform.backend.common.aspect
 * @Description: TODO
 * @Version 1.0
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class AuditAspect {

    private final Javers javers;
    private final List<AuditLog> auditTimeLine = new CopyOnWriteArrayList<>();
    private final Map<String, Object> dataStore = new ConcurrentHashMap<>();
    private final AtomicLong auditSequence = new AtomicLong(0);

    @Around("@annotation(audit)")
    public Object around(ProceedingJoinPoint joinPoint, Audit audit) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String[] paramNames = signature.getParameterNames();
        Object[] args = joinPoint.getArgs();

        // 提取ID
        String entityId = extractEntityId(args, paramNames, audit);
        if (entityId == null) {
            log.warn("跳过审计：未找到实体ID");
            return joinPoint.proceed();
        }

        // 获取执行前快照
        Object before = dataStore.get(entityId);

        Object result = joinPoint.proceed();

        // 获取执行后快照
        Object after = args[audit.entityIndex()];

        Audit.ActionType actionType = audit.action();
        Diff diff = javers.compare(before, after);

        // 记录审计日志
        recordAudit(after != null ? after.getClass().getSimpleName() : "Unknown",
                entityId, actionType.name(), extractActor(args, paramNames, audit),
                javers.getJsonConverter().toJson(diff));

        if (actionType != Audit.ActionType.DELETE) {
            dataStore.put(entityId, after);
        } else {
            dataStore.remove(entityId);
        }

        return result;
    }

    private String extractEntityId(Object[] args, String[] names, Audit audit) {
        if (!audit.idParam().isEmpty()) {
            for (int i = 0; i < names.length; i++) {
                if (names[i].equals(audit.idParam())) {
                    return args[i].toString();
                }
            }
        }
        return null;
    }

    private String extractActor(Object[] args, String[] names, Audit audit) {
        if (!audit.actorParam().isEmpty()) {
            for (int i = 0; i < names.length; i++) {
                if (names[i].equals(audit.actorParam())) {
                    return args[i].toString();
                }
            }
        }
        return "system";
    }

    private void recordAudit(String entity, String id, String action, String actor, String diffJson) {
        AuditLog logEntry = new AuditLog(
                String.valueOf(auditSequence.incrementAndGet()),
                entity,
                id,
                action,
                actor,
                Instant.now(),
                diffJson);
        auditTimeLine.add(logEntry);
        log.info("审计记录：{}", logEntry);
    }
}
